﻿
Imports std
Imports multilock_t = multilock(Of locks.eventCombLock)
Imports lock_t = lock(Of locks.monitorlock)

Partial Public Class fceslite4(Of THREADSAFE As _boolean)
    Private Shared ReadOnly ts As Boolean = False
    Private ReadOnly cs As vector(Of cluster4) = Nothing
    Private ReadOnly free_cs As qless(Of cluster4) = Nothing
    Private ReadOnly vd As virtdisk = Nothing
    Private ReadOnly ml As multilock_t
    Private l As lock_t

    Shared Sub New()
        ts = +(alloc(Of THREADSAFE)())
    End Sub

    Public Sub New()
        cs = New vector(Of cluster4)()
        free_cs = New qless(Of cluster4)()
        vd = New virtdisk()
        If ts Then
            ml = New multilock_t()
        End If
    End Sub

    Private Function lock() As Boolean
        If ts Then
            l.wait()
        End If
        Return True
    End Function

    Private Function unlock() As Boolean
        If ts Then
            l.release()
        End If
        Return True
    End Function

    Private Function lock(ByVal id As Int64) As Boolean
        If ts Then
            lock()
            ml.lock(id)
            unlock()
        End If
        Return True
    End Function

    Private Function unlock(ByVal id As Int64) As Boolean
        If ts Then
            ml.release(id)
        End If
        Return True
    End Function

    Private Sub closeImpl()
        vd.close()
        cs.clear()
        free_cs.clear()
    End Sub

    Public Sub close()
        lock()
        closeImpl()
        unlock()
    End Sub

    Public Function flush() As Boolean
        Return vd.flush()
    End Function

    Public Function open(ByVal filename As String, Optional ByVal buffSize As Int32 = 4096) As Boolean
        Dim rtn As Boolean = False
        lock()
        closeImpl()
        If vd.open(filename, buffSize) Then
            Dim offset As Int64 = 0
            Dim id As Int64 = 0
            Dim c As cluster4 = Nothing
            While cluster4.ctor(vd, offset, id, c)
                id += 1
                offset = c.end_diskoffset()
                assert(offset <= vd.size())
                cs.push_back(c)
                If c.free() Then
                    free_cs.push(c)
                End If
            End While

            If offset < vd.size() Then
                raiseError(errorHandle.errorType.exclamation,
                           "found invalid cluster structure at virtdisk ", filename, " position ", offset,
                           ", ignore following data")
            End If

            rtn = True
        Else
            raiseError(errorHandle.errorType.exclamation, "cannot open virtdisk ", filename)
        End If
        unlock()
        Return rtn
    End Function

    Private Function locked_begin(ByVal id As Int64) As _do(Of Boolean)
        Return Function() As Boolean
                   Return lock(id) AndAlso
                          goto_next()
               End Function
    End Function

    Private Function locked_end(ByVal id As Int64) As Boolean
        Return unlock(id) AndAlso
               goto_end()
    End Function

    Private Function locked(ByVal ec As eventComb, ByVal id As Int64) As eventComb
        assert(Not ec Is Nothing)
        If ts Then
            Return New eventComb(locked_begin(id),
                                 Function() As Boolean
                                     Return waitfor(ec) AndAlso
                                            goto_next()
                                 End Function,
                                 Function() As Boolean
                                     Return locked_end(id) AndAlso
                                            ec.end_result()
                                 End Function)
        Else
            Return ec
        End If
    End Function

    Private Function locked(ByVal d As _do(Of Boolean), ByVal id As Int64) As eventComb
        If ts Then
            assert(Not d Is Nothing)
            Return New eventComb(locked_begin(id),
                                 Function() As Boolean
                                     Return d() AndAlso
                                            locked_end(id)
                                 End Function)
        Else
            Return New eventComb(Function() As Boolean
                                     Return d() AndAlso goto_end()
                                 End Function)
        End If
    End Function

    Public Function sizeof(ByVal id As Int64, ByVal size As pointer(Of Int64)) As eventComb
        Dim s As Int64 = 0
        Return locked(Function() As Boolean
                          Return sizeof_cluster(id, s) AndAlso
                                 eva(size, s)
                      End Function,
                      id)
    End Function

    Public Function sizeof(ByVal id As Int64, ByRef size As Int64) As Boolean
        assert(Not ts)
        Return sizeof_cluster(id, size)
    End Function

    Public Function read(ByVal id As Int64, ByVal buff As pointer(Of bytes)) As eventComb
        Dim rtn As bytes = Nothing
        Dim nonempty_count As Int64 = 0
        Dim useds() As Int64 = Nothing
        Dim offsets() As Int64 = Nothing
        Dim bs() As pointer(Of bytes) = Nothing
        Dim recs() As eventComb = Nothing

        Dim finish As _do(Of Boolean, Boolean) =
            Function(ByRef b As Boolean) As Boolean
                If b Then
                    eva(buff, rtn)
                    Return goto_end()
                Else
                    eva(buff, CType(Nothing, bytes))
                    Return False
                End If
            End Function
        Return locked(New eventComb(Function() As Boolean
                                        If valid_head_cluster(id) Then
                                            assert(each_nonempty_cluster_used(id, useds, nonempty_count))
                                            If nonempty_count = 0 Then
                                                Return finish(True)
                                            Else
                                                useds_offsets(useds, offsets)
                                                Dim size As Int64 = 0
                                                assert(sizeof_cluster(id, size))
                                                rtn = New bytes()
                                                rtn.size() = size
                                                ReDim bs(nonempty_count - 1)
                                                For i As Int64 = 0 To bs.Length() - 1
                                                    bs(i) = New pointer(Of bytes)()
                                                Next
                                                ReDim recs(nonempty_count - 1)
                                                If foreach_nonempty_cluster(
                                                       id,
                                                       Function(ByRef c As cluster4,
                                                                ByRef i As Int64) As Boolean
                                                           If i = nonempty_count Then
                                                               Return False
                                                           Else
                                                               Return c.read(bs(i), recs(i)) AndAlso
                                                                      eva(i, i + 1)
                                                           End If
                                                       End Function) Then
                                                    Return waitfor(recs) AndAlso
                                                           goto_next()
                                                Else
                                                    Return finish(False)
                                                End If
                                            End If
                                        Else
                                            Return finish(False)
                                        End If
                                    End Function,
                             Function() As Boolean
                                 If foreach_nonempty_cluster(id,
                                                             Function(ByRef c As cluster4,
                                                                      ByRef i As Int64) As Boolean
                                                                 If i = nonempty_count OrElse
                                                                    Not recs(i).end_result() OrElse
                                                                    +bs(i) Is Nothing OrElse
                                                                    (+bs(i)).size() <> useds(i) Then
                                                                     Return False
                                                                 Else
                                                                     memcpy(+rtn, offsets(i), ++bs(i))
                                                                     Return True
                                                                 End If
                                                             End Function) Then
                                     Return finish(True)
                                 Else
                                     Return finish(False)
                                 End If
                             End Function),
                      id)
    End Function

    Public Function write(ByVal id As Int64, ByVal buff As bytes) As eventComb
        Dim ec As eventComb = Nothing
        Return locked(New eventComb(Function() As Boolean
                                        If valid_head_cluster(id) AndAlso
                                           deleteImpl(id, False) Then
                                            ec = appendImpl(id, buff)
                                            Return waitfor(ec) AndAlso
                                                   goto_next()
                                        Else
                                            Return False
                                        End If
                                    End Function,
                                    Function() As Boolean
                                        Return ec.end_result() AndAlso
                                               goto_end()
                                    End Function),
                      id)
    End Function

    Private Function appendImpl(ByVal id As Int64, ByVal buff As bytes) As eventComb
        Dim c As cluster4 = Nothing
        Dim wec As eventComb = Nothing
        Dim offset As Int64 = 0
        Return New eventComb(Function() As Boolean
                                 If valid_head_cluster(id) AndAlso Not buff Is Nothing Then
                                     If buff.size() = 0 Then
                                         Return goto_end()
                                     Else
                                         Dim n As cluster4 = Nothing
                                         c = cs(id)
                                         While find_nextCluster(c, n)
                                             c = n
                                         End While
                                         assert(Not c Is Nothing)
                                         If c.full() Then
                                             c = find_next_free_cluster_and_chain(buff.size(), c)
                                         End If
                                         Return goto_next()
                                     End If
                                 Else
                                     Return False
                                 End If
                             End Function,
                             Function() As Boolean
                                 assert(Not c Is Nothing)
                                 assert(offset < buff.size())
                                 Dim finish As _do(Of Boolean) =
                                     Function() As Boolean
                                         Return waitfor(wec) AndAlso
                                                goto_next()
                                     End Function
                                 assert(c.remain() > 0)
                                 If c.remain() < buff.size() - offset Then
                                     Return c.append(New bytes(+buff, offset, c.remain()), wec) AndAlso
                                            eva(offset, offset + c.remain()) AndAlso
                                            finish()
                                 Else
                                     Return c.append(New bytes(+buff, offset), wec) AndAlso
                                            eva(offset, buff.size()) AndAlso
                                            finish()
                                 End If
                             End Function,
                             Function() As Boolean
                                 If wec.end_result() Then
                                     If offset < buff.size() Then
                                         c = find_next_free_cluster_and_chain(buff.size() - offset, c)
                                         Return goto_prev()
                                     Else
                                         Return goto_end()
                                     End If
                                 Else
                                     Return False
                                 End If
                             End Function)
    End Function

    Public Function append(ByVal id As Int64, ByVal buff As bytes) As eventComb
        Return locked(appendImpl(id, buff), id)
    End Function

    Private Function deleteImpl(ByVal id As Int64,
                                Optional ByVal free_head_cluster As Boolean = True) As Boolean
        Return valid_head_cluster(id) AndAlso
               foreach_cluster(id,
                               Function(ByRef c As cluster4, ByRef i As Int64) As Boolean
                                   If c.delete() Then
                                       If free_head_cluster OrElse i > 0 Then
                                           free_cs.push(c)
                                       End If
                                       Return True
                                   Else
                                       Return False
                                   End If
                               End Function)
    End Function

    Public Function delete(ByVal id As Int64) As eventComb
        Return locked(New eventComb(Function() As Boolean
                                        Return deleteImpl(id, True) AndAlso
                                               goto_end()
                                    End Function),
                      id)
    End Function

    Public Function allocate(ByVal size As Int64, ByRef id As Int64) As Boolean
        id = find_next_free_cluster(size).clusterId()
        Return True
    End Function
End Class
